#!/bin/bash
#
# Copyright 2015-2017 Adrian DC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

# === Standalone Source Helper ===
# source <(curl -Ls https://github.com/AdrianDC/android_development_shell_tools/raw/master/android_adb_host.rc)
# source <(curl -Ls https://github.com/AdrianDC/android_development_shell_tools/raw/master/android_adb_tools.rc)
# source <(curl -Ls https://github.com/AdrianDC/android_development_shell_tools/raw/master/android_adb_installers.rc)

# === ADB File Pusher ===
function adbpushfile()
{
  # Usage
  if [ -z "${1}" ] || [ -z "${2}" ]; then
    echo '';
    echo ' Usage: adbpushfile <file_path> <file_target> (Push files through adb)';
    echo '';
    return;
  fi;

  # Variables
  local filepath=${1};
  local filetarget=${2};
  local filedir;

  # Debug target file
  echo -e "\e[1;36m # ${filetarget}";
  echo -en '\e[1;33m | ';
  adb shell "ls -l '${filetarget}'";

  # Push file to target
  if [ -f "${filepath}" ]; then
    echo -en '\e[1;32m | ';
    adb push "${filepath}" "${filetarget}";

  # Create symlink on target
  elif [ -L "${filepath}" ]; then
    filedir=$(dirname "${filetarget}");
    filepath=$(readlink "${filepath}");
    echo -en '\e[1;32m | ';
    adb shell "mkdir -p '${filedir}'";
    adb shell "ln -fs '${filepath}' '${filetarget}'";
    adb shell "ls -l '${filetarget}'" | tr -s ' ' | cut -d' ' -f 9-;

  # Delete missing file
  else
    echo -en "\e[1;31m | Removing '${filetarget}' : ";
    adb shell "if [ -f '${filetarget}' ]; then rm '${filetarget}'; echo 'Done.'; else echo 'No such file on the target...'; fi;";
  fi;

  # Restore colours
  echo -e '\e[0m';
}

# === ADB System File Listener / Installer ===
function adbif()
{
  # Usage
  if [ -z "${1}" ]; then
    echo '';
    echo ' Usage: adbif <command...> (Modules build listener and installed)';
    echo '';
    return;
  fi;

  # Variables
  local repo_top;
  local tmp_file;
  local tmp_installs;

  # Execute input command
  tmp_file=$(mktemp);
  "${@}" | tee "${tmp_file}";
  echo '';

  # Find new files
  repo_top=$(gettop 2> /dev/null);
  tmp_installs=$(grep -a 'target/product/[^\/]*/system' "${tmp_file}" \
               | sort \
               | uniq);

  # Install new files
  for file_path in ${tmp_installs[*]}; do
    echo "${file_path}";
    if [[ "${file_path}" == *'/system'* ]] && [[ ! "${file_path}" == *'/NOTICE_FILES'* ]]; then
      file_path=$(echo -n "${file_path}" | tail -1 \
                | sed 's/\x1B\[[0-9;]*[JKmsu]//g' \
                | sed 's/.*\/\([^\[]*.zip\).*/\1/g');
      if [ ! -f "${file_path}" ]; then
        file_path="${repo_top}/${file_path}";
      fi;
      if [ -f "${file_path}" ]; then
        adbpf "${file_path}";
      fi;
    fi;
  done;

  # End of process
  rm -f "${tmp_file}";
  echo '';
}

# === ADB System File Listener / Lister ===
function adbil()
{
  # Usage
  if [ -z "${1}" ]; then
    echo '';
    echo ' Usage: adbil <command...> (Modules build listener and lister)';
    echo '';
    return;
  fi;

  # Variables
  local packages='';
  local tmp_file;
  local tmp_installs;

  # Execute input command
  tmp_file=$(mktemp);
  "${@}" | tee "${tmp_file}";
  echo '';

  # Find new files
  tmp_installs=$(grep -a "target/product/[^\/]*/system" "${tmp_file}" \
               | sort \
               | uniq);

  # List new files
  for file_path in ${tmp_installs[*]}; do
    if [[ "${file_path}" == *'/system'* ]] && [[ ! "${file_path}" == *'/NOTICE_FILES'* ]]; then
      file_path=$(echo -n "${file_path}" | tail -1 \
                | sed 's/\x1B\[[0-9;]*[JKmsu]//g' \
                | sed 's/.*\/\([^\[]*.zip\).*/\1/g');
      if [ ! -f "${file_path}" ] && [ ! "${file_path:0:1}" = '/' ]; then
        file_path=$(gettop)/${file_path};
      fi;
      if [ -f "${file_path}" ]; then
        echo '';
        echo -e " \e[1;36m${file_path}\e[0m";
        packages+="${file_path} ";
      fi;
    fi;
  done;

  # Prepare public PACKAGES_RESULTS
  export PACKAGES_RESULTS="${packages}";

  # End of process
  rm -f "${tmp_file}";
  echo '';
}

# === ADB File Installer Helpers ===
alias adbi='adbro; adbif';

# === ADB File Pusher ===
function adbpf()
{
  # Usage
  if [ -z "${1}" ]; then
    echo '';
    echo ' Usage: adbpf <file_paths> (Advanced recursive adb files pusher)';
    echo '';
    return;
  fi;

  # Variables
  local filepath=;
  local filepathext=;
  local filepathtest=;
  local filetarget=;

  # Input files
  if [ -z "${recursive_call}" ]; then
    echo '';
  fi;
  for filepath in "${@}"; do

    # Extract full paths
    if [ -L "$(pwd)/${filepath}" ]; then
      filepath=$(pwd)/${filepath};
    elif [ -L "${filepath}" ]; then
      filepath=${filepath};
    elif [ -e "${filepath}" ]; then
      filepath=$(readlink -f "${filepath}");
    fi;
    filepathtest=${filepath};
    filepathext=${filepathtest##*.};

    # Iterate sub-folders
    if [ -d "${filepath}" ]; then
      recursive_call=true;
      adbpf "${filepath}/"*;
      recursive_call=;

    # Handle files
    else

      # Detect file targets
      if [ "${filepathext}" = 'mk' ] || [ "${filepathext}" = 'py' ]; then
        continue;
      fi;
      if [[ "${filepathtest}" == *'proprietary/'* ]]; then
        filetarget=$(echo -n "${filepathtest}" | sed "s/.*proprietary\/\(.*\)/\/system\/\1/g");
        filepathtest=${filetarget};
      fi;
      if [[ "${filepathtest}" == *'vendor/'* ]]; then
        filetarget=$(echo -n "${filepathtest}" | sed "s/.*vendor\/\(.*\)/\/system\/vendor\/\1/g");
        filepathtest=${filetarget};
      fi;
      if [[ "${filepathtest}" == *'configs/'* ]]; then
        filetarget=$(echo -n "${filepathtest}" | sed "s/.*configs\/\(.*\)/\/system\/etc\/\1/g");
        filepathtest=${filetarget};
      fi;
      if [[ "${filepathtest}" == *'system/'* ]]; then
        filetarget=$(echo -n "${filepathtest}" | sed "s/.*\(system\/.*\)/\/\1/g");
        filepathtest=${filetarget};
      fi;
      if [[ "${filepathtest}" == "${filepath}" ]]; then
        if [[ "${filepathtest}" == *'.xml'* ]] || [[ "${filepathtest}" == *'.conf'* ]]; then
          filetarget=/system/etc/$(basename "${filepath}");
        elif [[ "${filepathtest}" == *'.so'* ]]; then
          filetarget=/system/lib/$(basename "${filepath}");
        else
          filetarget=$(echo -n "${filepath}" | sed "s/.*\(system.*\)/\/\1/g");
        fi;
      fi;

      # Push file
      adbpushfile "${filepath}" "${filetarget}";
    fi;
  done;
}

# === ADB Pushes ===
alias adbp='adbro; adbpf';
alias adbpu='adbro; adbpushfile';

# === ADB Packages Results Pusher ===
function adbppr()
{
  # Usage: adbppr (adb automated PACKAGES_RESULTS files pusher)

  # Variables
  local result;
  local results=();

  # Parse PACKAGES_RESULTS string to array
  for result in ${PACKAGES_RESULTS}; do
    results+=("${result}");
  done;

  # adb automated PACKAGES_RESULTS files pusher
  adbp "${results[@]}";
}

# === ADB Sideload Helper ===
function adbside()
{
  # Usage
  if [ -z "${1}" ]; then
    echo '';
    echo ' Usage: adbside <file_zip> [boot_reboot] (adb sideload helper)';
    echo '';
    return;
  fi;

  # Variables
  local adb_cmd;
  local rebooted=;

  # Initialize adb
  echo '';
  adb_cmd=$(adbcmd);
  ${adb_cmd} devices;

  # Reboot to recovery if needed
  if ! timeout 2 sh -c "${adb_cmd} devices | grep -q 'recovery\|sideload'" 2> /dev/null; then
    echo -e '\e[1;33madbside: Rebooting to recovery\e[0m';
    ${adb_cmd} reboot recovery;
    rebooted=true;
  fi;

  # Wait for an adb recovery
  while true; do
    if timeout .5 sh -c "${adb_cmd} devices | grep -q 'recovery\|sideload'" 2> /dev/null; then
      break;
    fi;
    echo -e '\e[1;33madbside: Waiting for recovery or sideload\e[0m';
    sleep 2;
  done;

  # Wait for an adb sideload
  while true; do
    if timeout .5 sh -c "${adb_cmd} devices | grep -q 'sideload'" 2> /dev/null; then
      break;
    fi;

    # Start twrp sideload
    echo -e '\e[1;33madbside: Waiting for sideload\e[0m';
    ${adb_cmd} shell twrp sideload 2> /dev/null;
    sleep 2;
  done;

  # Sideload the file
  ${adb_cmd} sideload "${1}";

  # Wait for an adb recovery
  while true; do
    if timeout .5 sh -c "${adb_cmd} devices | grep -q 'recovery'" 2> /dev/null; then
      break;
    fi;
    echo -e '\e[1;33madbside: Waiting for recovery\e[0m';
    sleep 2;
  done;

  # Reboot to Android if requested or forced reboot to recovery
  if [ -z "${2}" ] && [ -z "${rebooted}" ] || [ "${2}" = 'false' ]; then
    echo -e '\e[1;33madbside: Staying in recovery\e[0m';
  else
    echo -e '\e[1;33madbside: Rebooting to Android\e[0m';
    ${adb_cmd} reboot;
  fi;
  echo '';
}

# === ADB Sideload ROM with Addons Helper ===
function adbsiderom()
{
  # Usage
  if [ -z "${1}" ] || [ ! -f "${1}" ]; then
    echo '';
    echo ' Usage: adbsiderom <rom_zip_or_bootimage> <device> [boot_no_reboot] (adb sideload ROM with addons helper)';
    echo '';
    return;
  fi;

  # Variables
  local file_input=${1};
  local device=${2};
  local no_reboot=${3};
  local name;
  local path=${ANDROID_DEVELOPMENT_SHELL_TOOLS_WORKSPACE:-${HOME}}/.bash_android.rom.addons;
  local target_old;

  # Detect missing device input automatically
  if [ -z "${device}" ]; then
    file_input=$(readlink -f "${file_input}");
    for name in $(cd "${path}/"; echo ''*); do
      if [[ "${file_input}" == *"${name}"* ]]; then
        device=${name};
        break;
      fi;
    done;
  fi;

  # Device not identified, present usage
  if [ -z "${device}" ]; then
    adbsiderom '';
    return;
  fi;

  # ROM addons folder
  if [ ! -d "${path}/${device}" ]; then
    mkdir "${path}";
    mkdir "${path}/${device}";
    echo '';
    echo -e "\e[1;33madbsiderom: Fill the '${path}/${device}/' folder with zip Addons to flash after the ROM zip\e[0m";
    diropen "${path}/${device}";
    echo '';
    echo -n ' adbsiderom: Press [Enter] to continue... ';
    read -r;
  fi;

  # Launch adbside with ROM zip
  if [ "${file_input: -4}" = '.zip' ]; then
    echo '';
    echo -e "\e[1;32madbsiderom: Flashing '$(basename "${file_input}")' [${device}]\e[0m";
    adbside "${file_input}" false;

  # Launch adbbootpush with bootimage img
  elif [ "${file_input: -4}" = '.img' ]; then
    echo '';
    echo -e "\e[1;32madbsiderom: Pushing '$(basename "${file_input}")' bootimage [${device}]\e[0m";
    target_old=${TARGET_PRODUCT};
    export TARGET_PRODUCT=rom_${device};
    adbbootpush "${file_input}";
    export TARGET_PRODUCT=${target_old};
    echo '';
  fi;

  # Launch adbside for addons zip files
  for file in "${path}/${device}/"*'.zip'; do
    echo -e "\e[1;32madbsiderom: Flashing '$(basename "${file}")' [${device}]\e[0m";
    adbside "${file}" false;
  done;

  # Reboot to Android if requested
  if [ -z "${no_reboot}" ]; then
    echo -e '\e[1;32madbsiderom: Rebooting to Android\e[0m';
    $(adbcmd) shell reboot;
    echo '';
  fi;
}

# === ADB File Pusher and Installer ===
function adbpi()
{
  # Usage
  if [ -z "${1}" ] || [ ! -f "${1}" ]; then
    echo '';
    echo ' Usage: adbpi <file_path> (Automated file pusher and installer)';
    echo '';
    return;
  fi;

  # Variables
  local apkname;
  local filepath;
  local filetarget;

  # Set variables
  filepath=$(readlink -f "${1}");

  # Acquire root access
  sudo echo -n '';
  adbro;

  # Detect /system/ file
  if [[ "${filepath}" == *'/system/'* ]]; then

    # Push file to device
    echo '';
    filetarget=/system/${filepath#*/system/};
    adbpushfile "${filepath}" "${filetarget}";

    # Optimize .apk application
    if [ "${filetarget: -4}" = '.apk' ]; then
      apkname=$(aapt d xmltree "${filepath}" AndroidManifest.xml | grep -i 'package=');
      apkname=${apkname#*$'"'};
      apkname=${apkname%%$'"'*};
      if [ ! -z "${apkname}" ]; then
        adbu "${apkname}";
      else
        echo '';
      fi;
    fi;

  # Failed detection
  else
    echo '';
    echo " adbpi: '${filepath}' could not be handled";
    echo '';
  fi;
}

# === ADB Force Optimization ===
function adbu()
{
  # Usage
  if [ -z "${1}" ]; then
    echo '';
    echo ' Usage: adbu <package_name_or_file> (Force optimization of a package)';
    echo '';
    return;
  fi;

  # Variables
  local package=${1};
  local pid;

  # Parse input .apk file
  if [ -f "${package}" ] && [ "${package: -4}" = '.apk' ]; then
    package=$(aapt d xmltree "${package}" AndroidManifest.xml | grep -i 'package=');
    package=${package#*$'"'};
    package=${package%%$'"'*};
  fi;

  # Debug target file
  echo '';
  echo -e "\e[1;36m # '${package}' optimization";

  # Optimize application
  echo -en '\e[1;33m | Package optimizing... ';
  sudo adb shell pm force-dex-opt "${package}";
  echo -e '\e[1;32mDone';

  # Killing application
  echo -en '\e[1;33m | Killing application... ';
  sudo adb shell pkill "${package}";
  pid=$(sudo adb shell ps | grep "${package}" | awk '{ print $2 }');
  if [ -z "${pid}" ]; then
    pid=$(sudo adb shell ps -A | grep "${package}" | awk '{ print $2 }');
  fi;
  if [ ! -z "${pid}" ]; then
    sudo adb shell kill "${pid}" > /dev/null;
  fi;
  echo -e '\e[1;32mDone';

  # Restore colours
  echo -e '\e[0m';
}

# === ADB Git Commit Pusher ===
function adbgitpf()
{
  # Usage
  if [ -z "${1}" ]; then
    echo '';
    echo ' Usage: adbgitpf <commit_sha1> (Push files through adb from commit)';
    echo '';
    return;
  fi;

  # Variables
  local file_path;
  local tmpfile;

  # Push files through adb from commit
  adbro;
  tmpfile=$(mktemp);
  git diff-tree --no-commit-id --name-status -r "${1}" >"${tmpfile}";
  while read -r line; do
    file_path=$(git rev-parse --show-toplevel)/$(echo "${line}" | awk '{ print $2 }');
    if [[ "${file_path}" == *'.mk'* ]]; then
      continue;
    fi;
    adbpf "${file_path}";
  done < "${tmpfile}";
  rm -f "${tmpfile}";
}

# === MultiROM Development File Pusher ===
function adbpmrom()
{
  # Usage
  if [ -z "${1}" ]; then
    echo '';
    echo ' Usage: adbpmrom <file> (MultiROM development file pusher)';
    echo '';
    return;
  fi;

  # Variables
  local filepath=${1};
  local filetarget;

  # Target file selection
  if [ ! -z "${2}" ]; then
    filetarget=/sdcard/multirom/${2}/${1};
  else
    filetarget=/sdcard/multirom/${1};
  fi;

  # Push file
  adbpushfile "${filepath}" "${filetarget}";
}

# === MultiROM Encryption File Pusher ===
function adbpmromenc()
{
  # Usage
  if [ -z "${1}" ]; then
    echo '';
    echo ' Usage: adbpmromenc <file> (MultiROM development encryption file pusher)';
    echo '';
    return;
  fi;

  # MultiROM encryption file pusher
  adbpmrom "${1}" enc;
}

# === ADB apk Installers ===
function adbapkinstall()
{
  # Usage: adbapkinstall (Install available apk files from current path)

  # Install all available apk files
  for file in *.apk; do
    echo "-- ${file} --";
    adb install -r "${file}";
  done;
}
